網頁應用程式3

林嶔 (Lin, Chin)

Lesson 14

第一節:使控制區的參數隨著Server端的狀況而改變(1)

– 所以,如果可以的話,我們希望左側的控制區可以出現一個選單,列出使用者所上傳的檔案的所有的變項名稱,並且讓使用者能選擇他的自變項&依變項。

– 請在這裡下載練習用檔案

test2=read.table("Example.txt",header=TRUE) #讀取Example.txt,並存成"test2"這個物件(資料表格式)
head(test2,10)                                 #先簡單看下這個資料的樣子
##      Height   Weight      BMI Cholesterol Triglyceride
## 1  173.9095 69.30906 22.91623    196.8222     146.4670
## 2  171.1519 66.72796 22.77951    183.2731     152.1301
## 3  169.4500 69.74043 24.28855    193.6936     150.3699
## 4  164.4632 58.76008 21.72426    182.4610     140.6752
## 5  172.2093 64.85015 21.86743    186.5315     146.7873
## 6  166.4883 66.17726 23.87489    187.4913     165.1633
## 7  177.2201 92.62094 29.49057    211.4545     167.2411
## 8  177.6544 85.45003 27.07448    203.5222     166.3448
## 9  175.4248 89.43701 29.06267    208.3781     164.0171
## 10 163.2245 55.52598 20.84133    185.0684     149.5531
summary(test2)                                 #看"test2"這個資料表的所有變項的基本資訊
##      Height          Weight            BMI         Cholesterol   
##  Min.   :143.6   Min.   : 35.15   Min.   :13.66   Min.   :161.7  
##  1st Qu.:162.9   1st Qu.: 63.47   1st Qu.:22.54   1st Qu.:187.2  
##  Median :170.6   Median : 69.75   Median :24.62   Median :197.6  
##  Mean   :168.7   Mean   : 70.83   Mean   :24.75   Mean   :196.2  
##  3rd Qu.:173.7   3rd Qu.: 80.53   3rd Qu.:27.21   3rd Qu.:204.8  
##  Max.   :197.5   Max.   :103.20   Max.   :35.32   Max.   :219.6  
##   Triglyceride  
##  Min.   :114.0  
##  1st Qu.:149.7  
##  Median :158.6  
##  Mean   :158.6  
##  3rd Qu.:167.0  
##  Max.   :194.9

– 還記得如何做linear regression吧?

Result=lm(Weight~Height,data=test2) #linear regression test
Result
## 
## Call:
## lm(formula = Weight ~ Height, data = test2)
## 
## Coefficients:
## (Intercept)       Height  
##    -79.4208       0.8904
summary(Result)
## 
## Call:
## lm(formula = Weight ~ Height, data = test2)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -28.2430  -6.6118  -0.7133   6.7464  25.6051 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -79.42076   15.28220  -5.197 1.11e-06 ***
## Height        0.89043    0.09039   9.851 2.55e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.552 on 98 degrees of freedom
## Multiple R-squared:  0.4975, Adjusted R-squared:  0.4924 
## F-statistic: 97.04 on 1 and 98 DF,  p-value: 2.552e-16

– 畫張散佈圖吧!

plot(test2$Height,test2$Weight,pch=19)
abline(Result)

第一節:使控制區的參數隨著Server端的狀況而改變(1)

test2=read.table("Example.txt",header=TRUE) #讀取Example.txt,並存成"test2"這個物件(資料表格式)
colnames(test2)  # 列出所有的欄位名稱
## [1] "Height"       "Weight"       "BMI"          "Cholesterol" 
## [5] "Triglyceride"
colnames(test2)[1] # 列出第一個欄位名稱
## [1] "Height"
colnames(test2)[4] # 列出第四個欄位名稱(依此類推)
## [1] "Cholesterol"
Result1=lm(test2[,"Weight"]~test2[,"Height"])
summary(Result1)
## 
## Call:
## lm(formula = test2[, "Weight"] ~ test2[, "Height"])
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -28.2430  -6.6118  -0.7133   6.7464  25.6051 
## 
## Coefficients:
##                    Estimate Std. Error t value Pr(>|t|)    
## (Intercept)       -79.42076   15.28220  -5.197 1.11e-06 ***
## test2[, "Height"]   0.89043    0.09039   9.851 2.55e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.552 on 98 degrees of freedom
## Multiple R-squared:  0.4975, Adjusted R-squared:  0.4924 
## F-statistic: 97.04 on 1 and 98 DF,  p-value: 2.552e-16

範例

library(shiny)

shinyUI(pageWithSidebar(
  
  headerPanel("Linear regression for two continuous variables."), 
  
  sidebarPanel(
    fileInput(inputId="files", label=h4("Upload your data file:"), multiple=FALSE, accept="text/plain"),
    helpText("Note: you only can upload the .txt file."),
    uiOutput("choose_columns1"),   #這裡是關鍵
    uiOutput("choose_columns2")    #這裡是關鍵
  ),
  
  mainPanel(
    verbatimTextOutput("summary"),
    plotOutput("plot",width = "500px", height = "500px")
  )  
  
))
library(shiny)

shinyServer(function(input, output) {
  
  DATA <- reactive({
    if (is.null(input$files)) {return()} else {
      dat <- read.table(input$files$datapath,header=T)
      return(dat) 
    }
  })
  
  output$choose_columns1 <- renderUI({  #這裡是關鍵
    dat = DATA()
    if (is.null(dat)) {return()} else {
      colnames <- colnames(dat)
      selectInput("Y", h4("Choose a dependence variable:"), choices = colnames)
    }
  })
  
  output$choose_columns2 <- renderUI({  #這裡是關鍵
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)) {return()} else {
      colnames <- colnames(dat)
      selectInput("X", h4("Choose a independence variable:"), choices = colnames[which(colnames!=input$Y)])
    }
  })
  
  output$summary <- renderPrint({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      X <- dat[,input$X]  #這裡是關鍵
      Y <- dat[,input$Y]  #這裡是關鍵
      Result=lm(Y~X)
      return(summary(Result))
    }  
  })
  
  output$plot <- renderPlot({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      X <- dat[,input$X]  #這裡是關鍵
      Y <- dat[,input$Y]  #這裡是關鍵
      plot(X,Y,pch=19)
      abline(lm(Y~X),col="black")   
    }  
  })
  
})

練習-1

– 兩個連續變項,除了能做linear regression之外,還有Correlation可以做。

Result1=cor.test(test2[,"Weight"],test2[,"Height"],method="pearson") # Pearson correlation
Result1
## 
##  Pearson's product-moment correlation
## 
## data:  test2[, "Weight"] and test2[, "Height"]
## t = 9.8507, df = 98, p-value = 2.552e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.5907925 0.7920433
## sample estimates:
##       cor 
## 0.7053591
Result2=cor.test(test2[,"Weight"],test2[,"Height"],method="spearman") # Spearman correlation
Result2
## 
##  Spearman's rank correlation rho
## 
## data:  test2[, "Weight"] and test2[, "Height"]
## S = 49490, p-value < 2.2e-16
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##       rho 
## 0.7030303

– 請依照剛剛的範例,設計一個選單,讓使用者能指定要使用Pearson correlation還是Spearman correlation。

– 當然,能讓使用者改個散布圖的顏色也是必須的…

–- 請點這裡下載測試檔案

– 這份檔案中,有許多類別變項&連續變項

– 設計一個分析系統,讓使用者能自由的選擇任2個變數,並決定要做t test、ANOVA、correlation或卡方檢定。

x = sample(1:3,100,replace=T)
y = x*2 + rnorm(100)
anova(aov(y~factor(x)))  # ANOVA

demo_table=array(c(255,312,203,426),dim=c(2,2),dimnames=list(exposure=c("exp","non-exp"),disease=c("case","control")))
chisq.test(demo_table)  # Chi-square test

練習-1 (基礎版) 答案

library(shiny)

shinyUI(pageWithSidebar(
  
  headerPanel("Linear regression for two continuous variables."), 
  
  sidebarPanel(
    fileInput(inputId="files", label=h4("Upload your data file:"), multiple=FALSE, accept="text/plain"),
    helpText("Note: you only can upload the .txt file."),
    uiOutput("choose_columns1"),   #這裡是關鍵
    uiOutput("choose_columns2"),   #這裡是關鍵
    radioButtons("method", "What is the method to analysis?", choices = c("Pearson correlation" = "pearson", "Spearman correlation" = "spearman")),
    radioButtons("Color", "Select the color of histogram:", choices = c("Red" = "red", "Blue" = "blue", "Green" = "green"))
  ),
  
  mainPanel(
    verbatimTextOutput("summary"),
    plotOutput("plot",width = "500px", height = "500px")
  )  
  
))
library(shiny)

shinyServer(function(input, output) {
  
  DATA <- reactive({
    if (is.null(input$files)) {return()} else {
      dat <- read.table(input$files$datapath,header=T)
      return(dat) 
    }
  })
  
  output$choose_columns1 <- renderUI({  #這裡是關鍵
    dat = DATA()
    if (is.null(dat)) {return()} else {
      colnames <- colnames(dat)
      selectInput("Y", h4("Choose a dependence variable:"), choices = colnames)
    }
  })
  
  output$choose_columns2 <- renderUI({  #這裡是關鍵
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)) {return()} else {
      colnames <- colnames(dat)
      selectInput("X", h4("Choose a independence variable:"), choices = colnames[which(colnames!=input$Y)])
    }
  })
  
  output$summary <- renderPrint({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      X <- dat[,input$X]  #這裡是關鍵
      Y <- dat[,input$Y]  #這裡是關鍵
      Result=cor.test(X,Y,method=input$method)
      return(Result)
    }  
  })
  
  output$plot <- renderPlot({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      X <- dat[,input$X]  #這裡是關鍵
      Y <- dat[,input$Y]  #這裡是關鍵
      plot(X,Y,pch=19,col=input$Color)
      abline(lm(Y~X),col="black")   
    }  
  })
  
})

第二節:增加介面複雜度(1)

##                  Functions    Characteristic
## 1        pageWithSidebar()    Main structure
## 2              fluidPage()    Main structure
## 3             navbarPage()    Main structure
## 4 tabsetPanel()+tabPanel()          Sub page
## 5           navlistPanel()   navigation list
## 6      fluidRow()+column() Splitted function
## 7          absolutePanel()     movable panel

– 請在這裡下載介面參數範例

練習-2

  1. 產生兩個分頁,第一個分頁為主要的運算區域,第二個分頁為軟體開發者的資訊(可以鑲入國防醫學院的網頁)
  2. 在第一個分頁中,讓用戶選擇是否要使用範例DATA,並且將控制物件放在頁面的最上方,而輸出結果放在下方。(需要用到conditionalPanel())

– 範例DATA可以用下列程式碼獲得:

data(iris)
head(iris,10)

練習-2 答案

library(shiny)

shinyUI(navbarPage("Linear regression for two continuous variables.",
                   tabPanel("Analysis",
                            fluidRow(
                              column(4,
                                     h3(p(strong(span(style="color:blue","Step 1: Data selection")))),
                                     h4("Do you want to analyze your data?"),
                                     radioButtons("data","",c("No, I want to use example data." = "Example", "Yes, I want to analyze my data." = "Mydata")),
                                     conditionalPanel("input.data == 'Mydata'",
                                                      h4("Please upload your data file:"),
                                                      fileInput(inputId="files", label=h4("Upload your data file:"), multiple=FALSE, accept="text/plain"),
                                                      helpText("Note: you only can upload the .txt file.")
                                     )
                              ),
                              column(4,
                                     h3(p(strong(span(style="color:green","Step 2: Please select two variables")))),
                                     uiOutput("choose_columns1"),
                                     uiOutput("choose_columns2")
                              ),
                              column(4,
                                     h3(p(strong(span(style="color:red","Step 3: Please select method and color")))),
                                     radioButtons("method", "What is the method to analysis?", choices = c("Pearson correlation" = "pearson", "Spearman correlation" = "spearman")),
                                     radioButtons("Color", "Select the color of histogram:", choices = c("Red" = "red", "Blue" = "blue", "Green" = "green"))
                              )
                            ),
                            hr(),
                            fluidRow(
                              column(6,
                                     tags$style(type='text/css', '#summary {background-color: rgba(255,255,0,0.40); color: blue;}'),
                                     verbatimTextOutput("summary")
                              ),
                              column(6,
                                     plotOutput("plot",width = "500px", height = "500px")
                              )
                            )
                   ),
                   tabPanel("Software information",
                            p(strong(h4(span(style="color:green","Basic information:")))),                      
                            p(strong("Software name:"), span(style="color:blue", "Test")),
                            p(strong("Contributors:"), tags$a(href="https://www.researchgate.net/profile/Chin_Lin8", "Chin Lin"), span(style="color:blue", "<xup6fup@gmail.com>")),
                            p(strong("Maintainer:"), tags$a(href="https://www.researchgate.net/profile/Chin_Lin8", "Chin Lin"), span(style="color:blue", "<xup6fup@gmail.com>")),
                            p(strong("License:"), tags$a(href="http://www.gnu.org/licenses/gpl-3.0.en.html", "GPL (>= 3)")),
                            p(strong("Provider:"), tags$a(href="http://www.ndmctsgh.edu.tw/", "National Defence Medical Center (NDMC)"))
                            )
))
library(shiny)

shinyServer(function(input, output) {
  
  DATA <- reactive({
    if (input$data=="Example") {
      data(iris)
      return(iris[,-5])
    } else {
      if (is.null(input$files)) {return()} else {
        dat <- read.table(input$files$datapath,header=T)
        return(dat) 
      }
    }
  })
  
  output$choose_columns1 <- renderUI({
    dat = DATA()
    if (is.null(dat)) {return()} else {
      colnames <- colnames(dat)
      selectInput("Y", h4("Choose a dependence variable:"), choices = colnames)
    }
  })
  
  output$choose_columns2 <- renderUI({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)) {return()} else {
      colnames <- colnames(dat)
      selectInput("X", h4("Choose a independence variable:"), choices = colnames[which(colnames!=input$Y)])
    }
  })
  
  output$summary <- renderPrint({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      X <- dat[,input$X]
      Y <- dat[,input$Y]
      Result=cor.test(X,Y,method=input$method)
      return(Result)
    }  
  })
  
  output$plot <- renderPlot({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      X <- dat[,input$X]
      Y <- dat[,input$Y]
      plot(X,Y,pch=19,col=input$Color)
      abline(lm(Y~X),col="black")   
    }  
  })
  
})

第三節:基本的Output種類(1)

##              Functions     Outputs Library
## 1         plotOutput()  basic plot   shiny
## 2        tableOutput()  html table   shiny
## 3         textOutput()        text   shiny
## 4 verbatimTextOutput()  R response   shiny
## 5           uiOutput() control bar   shiny
## 6    dataTableOutput()   datatable      DT
## 7         htmlOutput()   html form   shiny

– 請複製貼上下列的ui & server

library(shiny)
library(DT)

shinyUI(navbarPage(
  title = 'DataTable Options',
  tabPanel('Basic',     
           DT::dataTableOutput('ex1'),
           verbatimTextOutput('out1')),
  tabPanel('Select single cell',        
           DT::dataTableOutput('ex2')),
  tabPanel('Filter',      
           DT::dataTableOutput('ex3')),
  tabPanel('show/hide button',       
           DT::dataTableOutput('ex4')),
  tabPanel('Colorful',  
           DT::dataTableOutput('ex5'))
))
library(shiny)
library(DT)

data(iris)
dat = iris

shinyServer(function(input, output) {

  output$ex1 <- DT::renderDataTable({
    Result = DT::datatable(dat)
    return(Result)
  })
  
  output$out1 <- renderPrint({
    input$ex1_rows_selected
  })
  
  output$ex2 <- DT::renderDataTable({
    Result = DT::datatable(dat, selection = "single")
    return(Result)
  })
  
  output$ex3 <- DT::renderDataTable({
    Result = DT::datatable(dat, filter = 'top')
    return(Result)
  })
  
  output$ex4 <- DT::renderDataTable({
    Result = DT::datatable(
      dat, rownames = FALSE,
      extensions = 'ColVis', options = list(dom = 'C<"clear">lfrtip')
    )
    return(Result)
  })
  
  output$ex5 <- DT::renderDataTable({
    Result = DT::datatable(dat)
    Result = formatStyle(Result, 'Sepal.Length', fontWeight = styleInterval(5, c('normal', 'bold')))
    Result = formatStyle(Result, 'Sepal.Width', color = styleInterval(c(3.4, 3.8), c('white', 'blue', 'red')),
                         backgroundColor = styleInterval(3.4, c('gray', 'yellow')))
    Result = formatStyle(Result, 'Petal.Length', background = styleColorBar(dat$Petal.Length, 'steelblue'),
                         backgroundSize = '100% 90%', backgroundRepeat = 'no-repeat', backgroundPosition = 'center')
    Result = formatStyle(Result, 'Species', transform = 'rotateX(45deg) rotateY(20deg) rotateZ(30deg)',
                         backgroundColor = styleEqual(unique(iris$Species), c('lightblue', 'lightgreen', 'lightpink')))
    return(Result)
  })
  
})

第三節:基本的Output種類(2)

– ui.R

library(shiny)
library(DT)

fluidPage(
  
  h1('A Client-side Table'),
  
  fluidRow(
    column(6, DT::dataTableOutput('x1')),
    column(6, plotOutput('x2', width = "500px", height = "500px"))
  )
  
)

– server.R

library(shiny)
library(DT)

data(cars)
dat = cars

shinyServer(function(input, output, session) {
  
  output$x1 = DT::renderDataTable({
    Result = DT::datatable(dat)
    return(Result)
  })
  
  output$x2 = renderPlot({
    selection = as.numeric(input$x1_rows_selected)
    X = dat[,1]
    Y = dat[,2]
    plot(X,Y)
    if (length(selection)!=0) {points(X[selection],Y[selection], pch = 19, cex = 2, col = "red")}
  })
  
})

練習-3

– 請在這裡下載練習用檔案

– 現在,請利用Datatable,讓使用者能Highlight他所選重的個案

library(shiny)

shinyUI(pageWithSidebar(
  
  headerPanel("Linear regression for two continuous variables."), 
  
  sidebarPanel(
    fileInput(inputId="files", label=h4("Upload your data file:"), multiple=FALSE, accept="text/plain"),
    helpText("Note: you only can upload the .txt file."),
    uiOutput("choose_columns1"),   #這裡是關鍵
    uiOutput("choose_columns2"),   #這裡是關鍵
    radioButtons("method", "What is the method to analysis?", choices = c("Pearson correlation" = "pearson", "Spearman correlation" = "spearman")),
    radioButtons("Color", "Select the color of histogram:", choices = c("Red" = "red", "Blue" = "blue", "Green" = "green"))
  ),
  
  mainPanel(
    verbatimTextOutput("summary"),
    plotOutput("plot",width = "500px", height = "500px")
  )  
  
))
library(shiny)

shinyServer(function(input, output) {
  
  DATA <- reactive({
    if (is.null(input$files)) {return()} else {
      dat <- read.table(input$files$datapath,header=T)
      return(dat) 
    }
  })
  
  output$choose_columns1 <- renderUI({  #這裡是關鍵
    dat = DATA()
    if (is.null(dat)) {return()} else {
      colnames <- colnames(dat)
      selectInput("Y", h4("Choose a dependence variable:"), choices = colnames)
    }
  })
  
  output$choose_columns2 <- renderUI({  #這裡是關鍵
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)) {return()} else {
      colnames <- colnames(dat)
      selectInput("X", h4("Choose a independence variable:"), choices = colnames[which(colnames!=input$Y)])
    }
  })
  
  output$summary <- renderPrint({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      X <- dat[,input$X]  #這裡是關鍵
      Y <- dat[,input$Y]  #這裡是關鍵
      Result=cor.test(X,Y,method=input$method)
      return(Result)
    }  
  })
  
  output$plot <- renderPlot({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      X <- dat[,input$X]  #這裡是關鍵
      Y <- dat[,input$Y]  #這裡是關鍵
      plot(X,Y,pch=19,col=input$Color)
      abline(lm(Y~X),col="black")   
    }  
  })
  
})

練習-3 答案

library(shiny)

shinyUI(pageWithSidebar(
  
  headerPanel("Linear regression for two continuous variables."), 
  
  sidebarPanel(
    fileInput(inputId="files", label=h4("Upload your data file:"), multiple=FALSE, accept="text/plain"),
    helpText("Note: you only can upload the .txt file."),
    uiOutput("choose_columns1"),   #這裡是關鍵
    uiOutput("choose_columns2"),   #這裡是關鍵
    radioButtons("method", "What is the method to analysis?", choices = c("Pearson correlation" = "pearson", "Spearman correlation" = "spearman")),
    radioButtons("Color", "Select the color of histogram:", choices = c("Red" = "red", "Blue" = "blue", "Green" = "green"))
  ),
  
  mainPanel(
    verbatimTextOutput("summary"),
    fluidRow(
      column(6, DT::dataTableOutput('table')),
      column(6, plotOutput("plot",width = "500px", height = "500px"))
    )
  )  
  
))
library(shiny)

shinyServer(function(input, output) {
  
  DATA <- reactive({
    if (is.null(input$files)) {return()} else {
      dat <- read.table(input$files$datapath,header=T)
      return(dat) 
    }
  })
  
  output$choose_columns1 <- renderUI({  #這裡是關鍵
    dat = DATA()
    if (is.null(dat)) {return()} else {
      colnames <- colnames(dat)
      selectInput("Y", h4("Choose a dependence variable:"), choices = colnames)
    }
  })
  
  output$choose_columns2 <- renderUI({  #這裡是關鍵
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)) {return()} else {
      colnames <- colnames(dat)
      selectInput("X", h4("Choose a independence variable:"), choices = colnames[which(colnames!=input$Y)])
    }
  })
  
  output$summary <- renderPrint({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      X <- dat[,input$X]  #這裡是關鍵
      Y <- dat[,input$Y]  #這裡是關鍵
      Result=cor.test(X,Y,method=input$method)
      return(Result)
    }  
  })
  
  output$table = DT::renderDataTable({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      new.dat <- dat[,c(input$X,input$Y)]
      Result = DT::datatable(new.dat)
      return(Result)
    }
  })
  
  output$plot <- renderPlot({
    dat = DATA()
    if (is.null(dat)|is.null(input$Y)|is.null(input$X)) {return()} else {
      selection = as.numeric(input$table_rows_selected)
      X <- dat[,input$X]  #這裡是關鍵
      Y <- dat[,input$Y]  #這裡是關鍵
      plot(X,Y,pch=19,col=input$Color)
      abline(lm(Y~X),col="black")
      if (length(selection)!=0) {points(X[selection],Y[selection], pch = 19, cex = 2, col = input$Color)}
    }  
  })
  
})

小結

  1. renderUI及uiOutput
  2. 認識了更多UI模組
  3. 了解如何做UI的優化
  4. Data table的應用